/*
 * Copyright 2014 by État de Genève (Switzerland)
 * All rights reserved.
 *
 * Author: ELCA Informatique SA
 */
define([
        'durandal/system',
        'toastr',
        'services/layout',
        'common/errorDialog',
        'i18n',
        'q',
        'common/locked',
        'services/config',
        'plugins/dialog'],
    function (system, toastr, L, ErrorDialog, i18n, Q, Locked, config, dialog) {

        var _notificationsTypes = {
            warning: 'warn',
            error: 'error',
            success: 'success',
            info: 'info'
        };

        var _defaultIcon = {};
        _defaultIcon[_notificationsTypes.warning] = 'interface-59';
        _defaultIcon[_notificationsTypes.error] = 'interface-57';
        _defaultIcon[_notificationsTypes.success] = 'status-7';
        _defaultIcon[_notificationsTypes.info] = 'interface-58';

        var notifHandler = {
            notifications: ko.observableArray(),

            getNotificationsShort: notificationsShort,

            // BUSINESS NOTIFICATIONS (top of the window)
            notifySuccess: notifySuccess,
            notifyError: notifyError,
            notifyInfo: notifyInfo,
            notifyWarning: notifyWarning,
            notify: notify,
            notifTypes: _notificationsTypes,

            clearNotifications: clear,

            ask: showConfirm,
            alert: showAlert,

            alreadyLocked: showLockedMessage,

            // TECHNICAL
            // error while loading a specific module
            viewNotFound: viewNotFound,
            // error popup
            error: showError,
            // action success (save, lock, etc...)
            success: showSuccess,
            info: showInfo,
            // dev log management
            log: log
        };

        return notifHandler;

        function showLockedMessage(lockedBy) {
            dialog.showBS3Dialog(new Locked(lockedBy));
        }

        /**
         * Show a yes or no confirmation popup.
         * currently uses javascript's default one, but can be customized to any other thanks to promises
         */
        function showConfirm(question) {
            return Q(confirm(question));
        }

        function showAlert(message) {
            return Q(alert(message));
        }

        /**
         * returns a subset of the notifications, with an upper count limit
         * Use it to not pollute the UI
         */
        function notificationsShort(nb) {

            var short = notifHandler.notifications.slice(0, nb);

            if (notifHandler.notifications().length > nb) {

                short.push({
                    icon: 'content-40',
                    message: i18n.ts('cms:error.moreAlerts', (notifHandler.notifications().length - nb)),
                    isError: false,
                    isWarning: true,
                    isInfo: false,
                    isSuccess: false
                });
            }

            return short;
        }

        function notifySuccess(message, icon) {
            notify(message, icon, _notificationsTypes.success);
        }

        function notifyError(message, icon) {
            notify(message, icon, _notificationsTypes.error);
        }

        function notifyInfo(message, icon) {
            notify(message, icon, _notificationsTypes.info);
        }

        function notifyWarning(message, icon) {
            notify(message, icon, _notificationsTypes.warning);
        }

        function notify(message, icon, type) {

            if (message.indexOf('cms:') === 0) {
                message = i18n.t(message);
            }

            type = (type || _notificationsTypes.info).toLowerCase();

            icon = icon || _defaultIcon[type];

            var notif = {
                icon: icon,
                message: message,
                isError: type === _notificationsTypes.error,
                isWarning: type === _notificationsTypes.warning,
                isInfo: type === _notificationsTypes.info,
                isSuccess: type === _notificationsTypes.success,
                close: function() {
                    var index = _.indexOf(notifHandler.notifications(), notif);
                    notifHandler.notifications.splice(index, 1);
                },
                timestamp: new Date().getTime()
            };

            notifHandler.notifications.push(notif);
        }

        /**
         * Removes all notifications
         */
        function clear() {

            var now = new Date().getTime();
            var recentOnes = _.filter(notifHandler.notifications(), function(notif) {
                return now - notif.timestamp < config.alerts.minTimeout;
            });

            notifHandler.notifications(recentOnes);

            //L.relayout();
        }

        /**
         * Standard error when some module could not be found
         */
        function viewNotFound(moduleName) {
            var e = new Error(i18n.ts('cms:error.viewNotFound', moduleName));
            e.code = 404;
            showError(e);
        }

        /**
         * Show a dialog with the errorObj fiels bound
         * @param errorObj
         * the errorObj should be an Error() object with optional additional fields:
         * - code:      error code, usual HTTP error code like 500, 404
         * - apiStack:  an object containing the stacktrace from Java, OR a string (containing HTML)
         * - req:       a string containing the original API request URL and params (if applicable)
         */
        function showError(errorObj) {

            // need a stack, title, message
            var title = i18n.ts('cms:error.errorDialogTitle', errorObj.code);
            var message = errorObj.message;
            var stack = errorObj.stack;

            // if error bubble from API, we have a backend stack too (can be HTML-string or collection of string)
            if (errorObj.apiStack) {
                var javaStack = '';
                if (typeof errorObj.apiStack !== 'string' && errorObj.apiStack.stacktrace) {

                    javaStack = errorObj.apiStack.stacktrace.join('\n');
                    delete errorObj.apiStack.stacktrace;
                }

                stack =
                    '----REQ: ' + errorObj.req
                        + '\n\n----API DATA:\n' + ko.toJSON(errorObj.apiStack, null, 2)
                        + '\n\n----JAVA STACK:\n' + javaStack
                        + '\n\n----JS STACK:\n' + stack;
            }

            // show dialog
            dialog.showBS3Dialog(new ErrorDialog(stack, title, message));
        }

        /**
         * Display a simple success message when everything when well
         */
        function showSuccess(message, title) {
            toastr.success(message, title);
        }

        function showInfo (message, title) {
            toastr.info(message, title);
        }

        /**
         * Used in many place of the code when an unexpected non blocking error appear.
         * Currently showing a simple notification, can be changed for production
         */
        function log() {

            // TODO: [?, ?] decide what to do with logs for production env.

            var args = Array.prototype.slice.call(arguments);

            // while in debug, report all the error everywhere!!
            if (system.debug()) {

                console.log(['[notifier log]'].concat(args));
                toastr.info(arguments[0], 'debug:log');
            }
        }

    });